local newt = require("newt")
local delt = require("delt")
local class = require("class")
local super = require("unknown")
local tqueue = require("vessel.tqueue")
local getZero = require("time.getZero")
local getWhatDay = require("time.getWhatDay")
local millisecond = require("time.millisecond")
local ctimer = require("single.ctimer")

---重复使用仓库
local store = reload("store")
local newt, delt = store()

---@class timer:unknown @定时管理器
local this = class(super)

---构造
function this:ctor()
    super.ctor(self)
    self._start = false
    ---单词执行
    self._maxloops = 200
    ---轮询间隔
    ---@type number
    self._interval = 100
    ---定时回调
    ---@type fun()
    self._callback = function(nownumber)
        self:callback(nownumber)
    end
    ---清理处理
    self:clear()
    ---定时函数
end

---启动函数
function this:launch()
    ---避免重复启动
    if self._start then
        return
    end
    self._start = true
    ---启动定时器
    self:timeout()
end

---清除数据
function this:clear()
    ---@type integer @暂停时间
    self._pauset = nil
    ---最小堆
    self._sort = tqueue.new()
    ---定时器
    ---@type table<string,number>
    self._names = {}
    ---定时器
    ---@type table<number,string>
    self._idens = {}
end

---获取时间
function this:time()
    return self.nownumber or millisecond()
end

function this:create(elapse, count, call, ...)
    local item = newt()
    item.elapse = elapse
    item.count = count
    item.call = call
    item.args = select("#", ...) > 0 and { ... } or nil
    return item
end

---定时回调
---@param  elapse   number      @流逝时间
---@param  count    number|nil  @回调次数
---@param  obj      table       @回调对象
---@param  call     fun()       @回调函数
---@return number @定时ID
function this:append(elapse, count, call, ...)
    local sort = self._sort
    local item = self:create(elapse, count, call, ...)
    local tick = self:time() + elapse
    local node = sort:create(tick, item)
    return sort:push_back(node, false)
end

---单次回调
---@param  elapse   number      @流逝时间
---@param  call     fun()    @回调函数
---@return number @定时ID
function this:appendCall(elapse, call, ...)
    return self:append(elapse, 1, call, ...)
end

---无限回调
---@param  elapse   number      @流逝时间
---@param  call     fun()    @回调函数
---@return number @定时ID
function this:appendEver(elapse, call, ...)
    return self:append(elapse, nil, call, ...)
end

---定时回调
---@param  name     string      @定时名字
---@param  elapse   number      @流逝时间
---@param  count    number|nil  @回调次数
---@param  obj      any         @回调对象
---@param  call     fun()       @回调函数
---@return number @定时ID
function this:appendBy(name, elapse, count, call, ...)
    local idens = self._idens
    local names = self._names
    local iden = names[name]
    local node = self._sort:auto_move(iden)

    if node then
        idens[iden] = nil
        self._sort:delt(node)
    end

    iden = self:append(elapse, count, call, ...)
    names[name] = iden
    idens[iden] = name
    return iden
end

---无限回调
---@param  name     string  @定时名字
---@param  elapse   number  @流逝时间
---@param  call     fun()   @回调函数
---@return number @定时ID
function this:appendEverBy(name, elapse, call, ...)
    return self:appendBy(name, elapse, nil, call, ...)
end

---定点回调
---@param  name     string      @定时名字
---@param  hour     number      @小时
---@param  minute   number      @分钟
---@param  second   number      @秒
---@param  call     fun()       @回调函数
function this:appendDay(name, hour, minute, second, call, ...)
    local now = millisecond() / 1000
    local sec = getZero(now)
    local nexsec = hour * 3600 + minute * 60 + second
    local daysec = sec + nexsec
    if daysec < now then
        ---今天时间已经过了
        daysec = daysec + 86400
    end

    ---添加执行事件
    local firstElapse = (daysec - now) * 1000
    local ulti        = self:time() + firstElapse
    local sort        = self._sort
    local item        = self:create(86400000, nil, call, ...)
    local node        = sort:create(ulti, item)
    sort:push_back(node, true)
    return node.auto
end

---定点回调()
---@param  list     date[]      @时刻列表
---@param  call     fun()    @回调函数
function this:appendDays(list, call, ...)
    for _, info in ipairs(list) do
        self:appendDay(info.name, info.hour, info.min, info.sec, call, ...)
    end
end

---周定时回调
---@param name     string      @定时名字
---@param week     number      @周几 (1 = 周一, 2 = 周二, ..., 7 = 周日)
---@param hour     number      @小时
---@param minute   number      @分钟
---@param second   number      @秒
---@param call     fun()     @回调函数
function this:appendWeek(name, week, hour, minute, second, call, ...)
    local now = millisecond() / 1000
    local sec = getZero(now)          -- 获取今天的零点时间
    local dayOfWeek = getWhatDay(now) -- 获取今天是周几 (1 = 周日, 2 = 周一, ..., 7 = 周六)

    -- 计算目标周几的日期
    local targetDay = (week % 7) + 1                      -- 将周日调整为 1，周一为 2，...，周六为 7
    local daysUntilNext = (targetDay - dayOfWeek + 7) % 7 -- 计算到下一个目标周几的天数

    -- 如果今天就是目标周几，且时间已经过了，则需要加 7 天
    if daysUntilNext == 0 then
        local targetTime = sec + (hour * 3600 + minute * 60 + second)
        if now < targetTime then
            daysUntilNext = 7
        end
    end

    -- 计算下一个目标时间的秒数
    local nextTargetTime = sec + (daysUntilNext * 86400) + (hour * 3600 + minute * 60 + second)
    ---添加执行事件
    local firstElapse    = (nextTargetTime - now) * 1000 -- 转换为毫秒
    local ulti           = self:time() + firstElapse
    local sort           = self._sort
    local item           = self:create(604800000, nil, call, ...)
    local node           = sort:create(ulti, item)
    sort:push_back(node, true)
    return node.auto
end

---删除定时
---@param iden    number      @定时器ID
function this:removeByID(iden)
    local idens = self._idens
    local names = self._names
    local tnode = self._sort:auto_move(iden)
    if tnode then
        ---回收数据
        delt(tnode.data)
        self._sort:delt(tnode)

        ---清除标识
        idens[iden] = nil

        ---全局标识
        local name = idens[iden]
        if name then
            names[name] = nil
        end
    end
end

---删除头部
function this:removeFirst(reder)
    local idens = self._idens
    local names = self._names

    if reder then
        ---获取标识
        local iden = reder.auto
        ---回收数据
        delt(reder.data)
        self._sort:delt(reder)

        ---清除标识
        idens[iden] = nil

        ---全局标识
        local name = idens[iden]
        if name then
            names[name] = nil
        end
    end
end

---删除定时
---@param name    number      @定时器标识
function this:removeByName(name)
    local iden = self._names[name]
    if not iden then
        return
    end
    return self:removeByID(iden)
end

---剩余时间
---@param iden number
function this:remaining(iden)
    local now = self._pauset or self:time()
    local node = self._sort:find_node(iden)
    if node then
        return node.ticks - now
    end
    return 0
end

---剩余时间
---@param name string @定时名字
function this:remainingBy(name)
    local names = self._names
    local iden = names[name]
    return self:remaining(iden)
end

---暂停定时
function this:pause()
    if not self._pauset then
        self._pauset = self:time()
    end
end

---暂停取消
function this:unpause()
    local pause = self._pauset

    if pause then
        self._pauset = nil
        local now = self:time()
        local dif = now - pause

        for _, link in ipairs(self._sort._uniqmap) do
            link.data.ticks = link.data.ticks + dif
        end

        --启动轮询
        self:callback(millisecond())
    end
end

---定时轮询
function this:callback(nownumber)
    self._nownumber = nownumber
    ---暂停
    if self._pauset then
        return
    end

    ---定时
    self:timeout()

    ---执行
    local count = 0
    repeat
        if self._maxloops then
            count = count + 1
            if count > self._maxloops then
                break
            end
        end
    until not self:execute(nownumber)
end

---轮询器
function this:timeout()
    ctimer.timeout(self._interval)(self._callback)
end

---设置间隔
---@param interval number @间隔
function this:interval(interval)
    self._interval = interval or self._interval
end

---最大循环(定时器太多)
---@param interval number @最大循环
function this:maxloops(maxloops)
    self._maxloops = maxloops
end

---检测器
---@param inow number @当前时间
---@param iend number @结束时间
function this:ifExpire(inow, iend)
    if inow < iend then
        return false
    end

    return true
end

---执行
---@return boolean
function this:execute(nownumber)
    ---@type vesselheap       @最大堆
    local csort = self._sort
    ---@type heapNode   @第一个
    local reder = csort:reder()

    if not reder then
        return false
    end

    ---@type number         @时间
    local inow = nownumber
    local iend = reder.ticks

    ---到期检查
    if not self:ifExpire(inow, iend) then
        return false
    end

    ---@type tagTimer   @定时器
    local item = reder.data
    ---@type number      @多少次
    local count = item.count

    if count then
        ---扣除次数
        item.count = count - 1
    end

    ---删除定时器
    csort:find_move(reder)
    if 0 ~= item.count then
        ---重新添加
        reder.ticks = reder.ticks + item.elapse
        csort:push_back(reder, false)
    else
        ---删除处理
        self:removeFirst(reder)
    end

    ---每次只调用一个定时
    local args = item.args
    if not args then
        item.call()
    else
        item.call(table.unpack(args))
    end
    return true
end

return this

---@class tagTimer              @定时数据
---@field elapse    number      @间隔时间
---@field count     number      @回调次数
---@field call      fun()       @回调函数
---@field args      any[]       @回调参数
